package cn.ztuo.bitrade.runnable;

import cn.ztuo.bitrade.Trade.CoinTrader;
import cn.ztuo.bitrade.entity.*;
import cn.ztuo.bitrade.service.ContractOrderService;
import cn.ztuo.bitrade.service.ContractService;
import cn.ztuo.bitrade.util.BigDecimalUtils;
import cn.ztuo.bitrade.util.ContractUtils;
import cn.ztuo.bitrade.util.HttpsUtil;
import cn.ztuo.bitrade.util.MessageResult;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.core.KafkaTemplate;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;

@Slf4j
public class RobotOrderRunnable implements Runnable {

    public static Map<String, Integer> robotStatusMap = new HashMap<>();
    private KafkaTemplate<String, String> kafkaTemplate;
    private static Map<String, Long> robotMap = new HashMap<>();
    private static Map<String, String> tidMap = new HashMap<>();
    private ContractOrderService orderService;
    private ContractCoin coin;
    private CoinTrader coinTrader;
    private ContractService contractService;
    private static Map<String, Map<ContractOrderDirection, List<ContractOrder>>> plateMap = new HashMap<>();


    private volatile static RobotOrderRunnable robotOrderRunnable;

    private RobotOrderRunnable() {

    }

    public static void setRoboStatusMap(Map<String, Integer> robotStatusMap) {
        RobotOrderRunnable.robotStatusMap = robotStatusMap;
    }

    private RobotOrderRunnable(KafkaTemplate<String, String> kafkaTemplate, ContractOrderService orderService, ContractCoin coin, CoinTrader coinTrader, ContractService contractService) {
        this.kafkaTemplate = kafkaTemplate;
        this.orderService = orderService;
        this.coin = coin;
        this.coinTrader = coinTrader;
        this.contractService = contractService;
    }

    public static RobotOrderRunnable getInstance(KafkaTemplate<String, String> kafkaTemplate, ContractOrderService orderService, ContractCoin coin, CoinTrader coinTrader, ContractService contractService) {

        robotOrderRunnable = new RobotOrderRunnable(kafkaTemplate, orderService, coin, coinTrader, contractService);

        return robotOrderRunnable;
    }

    private void robotAddOrder() {
        try {
            synchronized (coin) {
                log.info("合约机器人--" + coin.getSymbol() + "--下单中.......");
                if (robotStatusMap.get(coin.getSymbol()) == null || robotStatusMap.get(coin.getSymbol()) == 0) {
                    log.info(coin.getSymbol() + "=====机器人已经停止================");
                    return;
                }

                Ticker ticker = getTickerByUrl(coin);
                log.info("ticker = " + JSON.toJSONString(ticker));
                if (ticker == null) {
//                    robotStatusMap.put(coin.getSymbol(),0);
                    return;
                }

                log.info("匹配当前 最新价格  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
                ContractOrder orderBuyTrade = initRobotContractOrder(ContractOrderDirection.BUY, ticker);
                addOrder(orderBuyTrade);
                log.info("orderBuyTrade : " + JSON.toJSONString(orderBuyTrade));
                Thread.sleep(2 * 1000);
                ContractOrder orderSellTrade = initRobotContractOrder(ContractOrderDirection.SELL, ticker);
                addOrder(orderSellTrade);
                log.info("orderSellTrade : " + JSON.toJSONString(orderSellTrade));
                Thread.sleep(2 * 1000);
            }
        } catch (Exception e) {
            log.error(coin.getSymbol() + "机器人挂单异常={}", e);
        } finally {
            if (robotStatusMap.get(coin.getSymbol()) == null || robotStatusMap.get(coin.getSymbol()) == 0) {
                log.info(coin.getSymbol() + "=====机器人已经停止================");
                return;
            } else {
                MessageResult resutl = contractService.robotAddOrder(coin.getSymbol());
                log.info(resutl.getMessage());
                return;
            }
        }
    }

    private void addOrder(ContractOrder order) {
        log.info(coin.getSymbol() + "===开始匹配单=====================================================");
        MessageResult mr = orderService.addOrder(1L, order);
        if (mr.getCode() != 0) {
            log.info("提交订单失败:" + mr.getMessage());
            return;
        }
        kafkaTemplate.send("contract-order", order.getSymbol(), JSON.toJSONString(order));
        log.info(Thread.currentThread().getName() + "匹配单线程阻塞========================");
    }

    /**
     * 获取 OKEX 交易所的行情
     *
     * @return
     */
    private Ticker getTickerByUrl(ContractCoin coin) throws IOException {
        String symbol = coin.getSymbol();
        List<String> listHuobi = new ArrayList<>();
        listHuobi.add("BTCUSDT");
        listHuobi.add("ETHUSDT");
        listHuobi.add("EOSUSDT");
        listHuobi.add("LTCUSDT");
        listHuobi.add("BSVUSDT");

        List<String> listBitMax = new ArrayList<>();
        listBitMax.add("HTUSDT");
        listBitMax.add("OKBUSDT");
        listBitMax.add("BNBUSDT");

        JSONObject tick = null;
        Ticker ticker = null;
        JSONObject depth = null;
        List<ContractOrder> bids = new ArrayList<>();
        List<ContractOrder> asks = new ArrayList<>();
//        List<ContractOrder> delbids = new ArrayList<>();
//        List<ContractOrder> delasks = new ArrayList<>();
        if (listBitMax.contains(symbol)) {
            String bitmaxStr = HttpsUtil.doGet("https://bitmax.io/api/pro/v1/ticker?symbol=" + coin.getCoinSymbol() + "/" + coin.getBaseSymbol());
            String bnbDepthStr = HttpsUtil.doGet("https://bitmax.io/api/pro/v1/depth?symbol=" + coin.getCoinSymbol() + "/" + coin.getBaseSymbol());
//            String bnbDepthStr = readJsonFile("J:\\source\\bnbDepth.txt");
//            String bitmaxStr = readJsonFile("J:\\source\\bitMax.txt");
            JSONObject forObject = JSON.parseObject(bitmaxStr);
            if (forObject.getInteger("code") == 0) {
                JSONObject json = forObject.getJSONObject("data");
                ticker = new Ticker();
                ticker.setSymbol(coin.getSymbol());
                ticker.setTradePrice(json.getBigDecimal("close"));
                ticker.setTradeSize(json.getBigDecimal("volume").divide(coin.getMinVolume()).setScale(0, BigDecimal.ROUND_DOWN));
            } else {
                return null;
            }
            //添加盘口深度
            depth = JSON.parseObject(bnbDepthStr);
            if (depth.containsKey("code") && "0".equals(depth.getString("code"))) {
                tick = depth.getJSONObject("data").getJSONObject("data");
            }
        }
        if (!listBitMax.contains(symbol)) {
            String huobiStr = HttpsUtil.doGet("https://api.btcgateway.pro/swap-ex/market/trade?contract_code=" + coin.getCoinSymbol() + "-USD");
            String depthStr = HttpsUtil.doGet("https://api.btcgateway.pro/swap-ex/market/depth?contract_code=" + coin.getCoinSymbol() + "-USD&type=step0");
//            String huobiStr = readJsonFile("J:\\source\\huobiUSD.txt");
//            String depthStr = readJsonFile("J:\\source\\testRobot.txt");
            JSONObject jsonObject = JSON.parseObject(huobiStr);
            log.info("huobiStr : " + huobiStr);
            if (jsonObject.containsKey("status") && jsonObject.getString("status").equals("ok")) {
                List<JSONObject> objs = JSON.parseArray(jsonObject.getJSONObject("tick").getString("data"), JSONObject.class);
                JSONObject json = objs.get(0);
                ticker = new Ticker();
                ticker.setSymbol(coin.getSymbol());
                ticker.setTradePrice(json.getBigDecimal("price"));
                ticker.setTradeSize(json.getBigDecimal("amount"));
            } else {
                return null;
            }
            //添加盘口深度
            depth = JSON.parseObject(depthStr);
            if (depth.containsKey("status") && "ok".equals(depth.getString("status"))) {
                tick = depth.getJSONObject("tick");
            }
        }

        Ticker finalTicker = ticker;
        List<JSONArray> bidsArray = JSON.parseArray(tick.getString("bids"), JSONArray.class);
        List<JSONArray> asksArray = JSON.parseArray(tick.getString("asks"), JSONArray.class);
        if (bidsArray.size() > 20) {
            bidsArray = bidsArray.subList(bidsArray.size() - 19, bidsArray.size());
        }
        if (asksArray.size() > 20) {
            asksArray = asksArray.subList(0, 20);
        }
        bidsArray.stream().forEach(bid -> {
            ContractOrder order = new ContractOrder();
            order.setPrice(new BigDecimal(bid.get(0).toString()));
            order.setAmount(new BigDecimal(bid.get(1).toString()));
            order.setDirection(ContractOrderDirection.BUY);
            order.setType(ContractOrderType.LIMIT_PRICE);
            order.setTradedAmount(BigDecimal.ZERO);
            bids.add(order);
        });
        asksArray.stream().forEach(ask -> {
            ContractOrder order = new ContractOrder();
            order.setPrice(new BigDecimal(ask.get(0).toString()));
            order.setAmount(new BigDecimal(ask.get(1).toString()));
            order.setDirection(ContractOrderDirection.SELL);
            order.setType(ContractOrderType.LIMIT_PRICE);
            order.setTradedAmount(BigDecimal.ZERO);
            asks.add(order);
        });
//        bids.sort(Comparator.comparing(ContractOrder::getPrice).reversed());
//        asks.sort(Comparator.comparing(ContractOrder::getPrice));

        Map<ContractOrderDirection, List<ContractOrder>> mapPlate = new HashMap<>();
        if (!plateMap.isEmpty() && plateMap.containsKey(coin.getSymbol()) && plateMap.get(coin.getSymbol()) != null) {
            mapPlate = plateMap.get(coin.getSymbol());
            List<ContractOrder> bidsOld = mapPlate.get(ContractOrderDirection.BUY);
            List<ContractOrder> asksOld = mapPlate.get(ContractOrderDirection.SELL);

//            delbids = bidsOld.stream().filter(a->a.getPrice().compareTo(finalTicker.getTradePrice())>=0).collect(Collectors.toList());
//            if(delbids ==null || delbids.size()<=0){
//                delbids = bidsOld.subList(0,2);
//            }
//            delasks = asksOld.stream().filter(a->a.getPrice().compareTo(finalTicker.getTradePrice())<=0).collect(Collectors.toList());
//            if(delasks ==null || delasks.size()<=0){
//                delasks = bidsOld.subList(0,2);
//            }

            coinTrader.addRobotLimitPriceOrder(bids, ContractOrderDirection.BUY, coin);
            coinTrader.cancelRobotOrder(bidsOld, ContractOrderDirection.BUY, coin);
            coinTrader.addRobotLimitPriceOrder(asks, ContractOrderDirection.SELL, coin);
            coinTrader.cancelRobotOrder(asksOld, ContractOrderDirection.SELL, coin);
//            coinTrader.addRobotLimitPriceOrder(bids.subList(0,delbids.size()), ContractOrderDirection.BUY,coin);
//            coinTrader.cancelRobotOrder(delbids, ContractOrderDirection.BUY,coin);
//            coinTrader.addRobotLimitPriceOrder(asks.subList(0,delasks.size()), ContractOrderDirection.SELL,coin);
//            coinTrader.cancelRobotOrder(delasks, ContractOrderDirection.SELL,coin);
        } else {
            coinTrader.addRobotLimitPriceOrder(bids, ContractOrderDirection.BUY, coin);
            coinTrader.addRobotLimitPriceOrder(asks, ContractOrderDirection.SELL, coin);
        }
//        log.info("add bids size = "+ bids.subList(0,delbids.size()));
//        log.info("add asks size = "+ asks.subList(0,delasks.size()));
//        log.info("del bids size = "+ delbids.size());
//        log.info("del asks size = "+ delasks.size());
        mapPlate.put(ContractOrderDirection.BUY, bids);
        mapPlate.put(ContractOrderDirection.SELL, asks);
        plateMap.put(coin.getSymbol(), mapPlate);
        return ticker;
    }


    @Override
    public void run() {
        robotAddOrder();
    }

    private ContractOrder initRobotContractOrder(ContractOrderDirection direction, Ticker ticker) {
        ContractOrder order = new ContractOrder();
        order.setBaseSymbol(coin.getBaseSymbol());
        order.setCoinSymbol(coin.getCoinSymbol());
        order.setSymbol(coin.getSymbol());
        order.setOrderResource(ContractOrderResource.ROBOT);
        order.setType(ContractOrderType.LIMIT_PRICE);
        order.setDirection(direction);
        order.setMemberId(1L);
        order.setNowp(ticker.getTradeSize().setScale(0));
        order.setAmount(ticker.getTradeSize().multiply(coin.getMinVolume()));
        order.setPrice(ticker.getTradePrice().setScale(coin.getBaseCoinScale(), BigDecimal.ROUND_DOWN));
        //赋值binance最新价格
        order.setPositionFlag(ContractEnumType.ContractOrderPositionFlag.designate);

        ContractCoin bySymbol = coin;
        if (order.getDirection() == ContractOrderDirection.BUY) {
            if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.designate) {
                order.setPositionType(ContractOrderPositionType.RISE);
            }
        }
        if (order.getDirection() == ContractOrderDirection.SELL) {
            if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.designate) {
                order.setPositionType(ContractOrderPositionType.FALL);
            }
        }
        //挂单操作成交量范围控制
        //只有普通挂单才冻结相应保证金;
        //起始保证金
        JSONObject map = ContractUtils.getStart(order.getAmount(), bySymbol, order.getNowp(), order.getMemberId(), null);
        if (map == null) {
            return null;
        }
        BigDecimal start = map.getBigDecimal("start");
        String startRate = map.getString("startRate");
        log.info("====================起始保证金:  " + start);

        //维持保证金
        JSONObject maintainMap = ContractUtils.getMaintain(order.getAmount(), bySymbol, order.getNowp(), order.getMemberId(), null);
        if (maintainMap == null) {
            return null;
        }
        BigDecimal maintain = maintainMap.getBigDecimal("maintain");
        String maintainRate = maintainMap.getString("maintainRate");
        log.info("====================维持保证金:  " + maintain);
        order.setStart(start);
        order.setStartRate(startRate);
        order.setMaintain(maintain);
        order.setMaintainRate(maintainRate);
        order.setPositionValue(BigDecimalUtils.mul(order.getAmount(), order.getPrice()));
        order.setTurnover(BigDecimal.ZERO);
        order.setTradedAmount(BigDecimal.ZERO);
        order.setSetTime(Calendar.getInstance().getTimeInMillis());

        order.setMultiple(new BigDecimal(100));
        return order;
    }

    public static String readJsonFile(String fileName) {
        String jsonStr = "";
        try {
            File jsonFile = new File(fileName);
            FileReader fileReader = new FileReader(jsonFile);
            Reader reader = new InputStreamReader(new FileInputStream(jsonFile), "utf-8");
            int ch = 0;
            StringBuffer sb = new StringBuffer();
            while ((ch = reader.read()) != -1) {
                sb.append((char) ch);
            }
            fileReader.close();
            reader.close();
            jsonStr = sb.toString();
            return jsonStr;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    class Ticker {
        private Long id;
        private String symbol; //交易对
        private BigDecimal tradePrice; //最新成交价格
        private BigDecimal tradeSize; //最新成交数量
        private ContractOrderDirection direction;

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public ContractOrderDirection getDirection() {
            return direction;
        }

        public void setDirection(ContractOrderDirection direction) {
            this.direction = direction;
        }

        public String getSymbol() {
            return symbol;
        }

        public void setSymbol(String symbol) {
            this.symbol = symbol;
        }

        public BigDecimal getTradePrice() {
            return tradePrice;
        }

        public void setTradePrice(BigDecimal tradePrice) {
            this.tradePrice = tradePrice;
        }

        public BigDecimal getTradeSize() {
            return tradeSize;
        }

        public void setTradeSize(BigDecimal tradeSize) {
            this.tradeSize = tradeSize;
        }

    }
}